package collector

import (
	"encoding/xml"
	"log"
	"strings"
	"time"

	"gitee.com/openeuler/uos-libvirtd-exporter/slogx"
	"libvirt.org/go/libvirt"
	"libvirt.org/go/libvirtxml"
)

// LibvirtMetricsCollector implements MetricsCollector to fetch raw metrics from libvirt
type LibvirtMetricsCollector struct{}

// NewLibvirtMetricsCollector creates a new LibvirtMetricsCollector
func NewLibvirtMetricsCollector() *LibvirtMetricsCollector {
	return &LibvirtMetricsCollector{}
}

// CollectDomainInfo collects basic domain information from libvirt
func (mc *LibvirtMetricsCollector) CollectDomainInfo(
	conn *libvirt.Connect,
	domain *libvirt.Domain,
) (*DomainInfoMetrics, error) {
	domainInfo, err := domain.GetInfo()
	if err != nil {
		return nil, err
	}

	domainName, err := domain.GetName()
	if err != nil {
		return nil, err
	}

	domainUUID, err := domain.GetUUIDString()
	if err != nil {
		return nil, err
	}

	// Check if domain is persistent
	persistent, err := domain.IsPersistent()
	if err != nil {
		return nil, err
	}

	// Check if domain has managed save
	managedSave, err := domain.HasManagedSaveImage(0)
	if err != nil {
		return nil, err
	}

	// Check if domain is set to autostart
	autostart, err := domain.GetAutostart()
	if err != nil {
		return nil, err
	}

	metrics := &DomainInfoMetrics{
		Name:          domainName,
		UUID:          domainUUID,
		MemoryCurrent: float64(domainInfo.Memory) * 1024,
		MemoryMax:     float64(domainInfo.MaxMem) * 1024,
		CPUTime:       float64(domainInfo.CpuTime) / 1e9,
		Autostart:     autostart,
		Persistent:    persistent,
		ManagedSave:   managedSave,
	}

	// VM status metric
	if domainInfo.State == libvirt.DOMAIN_RUNNING {
		metrics.Status = 1.0
	} else {
		metrics.Status = 0.0
	}

	// Only collect uptime for running domains
	if domainInfo.State == libvirt.DOMAIN_RUNNING {
		domainTime, _, err := domain.GetTime(0)
		if err == nil {
			metrics.BootTime = time.Unix(int64(domainTime/1000), 0)
			metrics.Uptime = time.Since(metrics.BootTime).Seconds()
			metrics.HasUptime = true
		}
	}

	return metrics, nil
}

// CollectCPUStats collects CPU statistics from libvirt
func (mc *LibvirtMetricsCollector) CollectCPUStats(
	conn *libvirt.Connect,
	domain *libvirt.Domain,
) (*CPUStatsMetrics, error) {
	domainName, err := domain.GetName()
	if err != nil {
		return nil, err
	}

	domainUUID, err := domain.GetUUIDString()
	if err != nil {
		return nil, err
	}

	domainInfo, err := domain.GetInfo()
	if err != nil {
		return nil, err
	}

	// Get vCPU counts
	maxVCPUs, err := domain.GetMaxVcpus()
	if err != nil {
		return nil, err
	}

	// Get current vCPU count
	vcpuInfo, err := domain.GetVcpus()
	if err != nil {
		// If we can't get vcpu info, use a default
		vcpuInfo = make([]libvirt.DomainVcpuInfo, 0)
	}

	metrics := &CPUStatsMetrics{
		Name:         domainName,
		UUID:         domainUUID,
		VCPUsMax:     uint(maxVCPUs),
		VCPUsCurrent: uint(len(vcpuInfo)),
		CPUTime:      domainInfo.CpuTime,
	}

	return metrics, nil
}

// CollectMemoryStats collects memory statistics from libvirt
func (mc *LibvirtMetricsCollector) CollectMemoryStats(
	conn *libvirt.Connect,
	domain *libvirt.Domain,
) (*MemoryStatsMetrics, error) {
	domainName, err := domain.GetName()
	if err != nil {
		return nil, err
	}

	domainUUID, err := domain.GetUUIDString()
	if err != nil {
		return nil, err
	}

	// Get memory stats
	memStats, err := domain.MemoryStats(uint32(libvirt.DOMAIN_MEMORY_STAT_NR), 0)
	if err != nil {
		return nil, err
	}

	metrics := &MemoryStatsMetrics{
		Name: domainName,
		UUID: domainUUID,
	}

	// Parse memory stats
	for _, stat := range memStats {
		switch stat.Tag {
		case int32(libvirt.DOMAIN_MEMORY_STAT_ACTUAL_BALLOON):
			metrics.BalloonSize = stat.Val
		case int32(libvirt.DOMAIN_MEMORY_STAT_UNUSED):
			metrics.Unused = stat.Val
		case int32(libvirt.DOMAIN_MEMORY_STAT_AVAILABLE):
			metrics.Available = stat.Val
		case int32(libvirt.DOMAIN_MEMORY_STAT_RSS):
			metrics.RSS = stat.Val
		case int32(libvirt.DOMAIN_MEMORY_STAT_SWAP_IN):
			metrics.SwapIn = stat.Val
		case int32(libvirt.DOMAIN_MEMORY_STAT_SWAP_OUT):
			metrics.SwapOut = stat.Val
		case int32(libvirt.DOMAIN_MEMORY_STAT_MAJOR_FAULT):
			metrics.MajorFaults = stat.Val
		case int32(libvirt.DOMAIN_MEMORY_STAT_MINOR_FAULT):
			metrics.MinorFaults = stat.Val
		}
	}

	// Calculate total memory
	if metrics.Available > 0 && metrics.Unused > 0 {
		metrics.Total = metrics.Available
	}

	return metrics, nil
}

// CollectDiskStats collects disk I/O statistics from libvirt
func (mc *LibvirtMetricsCollector) CollectDiskStats(
	conn *libvirt.Connect,
	domain *libvirt.Domain,
) ([]DiskMetrics, error) {
	domainInfo, err := domain.GetInfo()
	if err != nil {
		return nil, err
	}

	// Only collect metrics for running domains
	if domainInfo.State != libvirt.DOMAIN_RUNNING {
		return []DiskMetrics{}, nil
	}

	domainName, err := domain.GetName()
	if err != nil {
		return nil, err
	}

	domainUUID, err := domain.GetUUIDString()
	if err != nil {
		return nil, err
	}

	var metrics []DiskMetrics

	// Try to discover devices dynamically
	devices := mc.discoverBlockDevices(domain)

	for _, device := range devices {
		// Get detailed block stats
		stats, err := domain.BlockStatsFlags(device, 0)
		if err != nil {
			// If we can't get extended stats, try basic stats
			basicStats, err := domain.BlockStats(device)
			if err != nil {
				continue
			}

			m := DiskMetrics{
				Name:       domainName,
				UUID:       domainUUID,
				Device:     device,
				Path:       "/dev/" + device,
				ReadBytes:  basicStats.RdBytes,
				WriteBytes: basicStats.WrBytes,
				ReadOps:    basicStats.RdReq,
				WriteOps:   basicStats.WrReq,
			}
			metrics = append(metrics, m)
		} else {
			m := DiskMetrics{
				Name:        domainName,
				UUID:        domainUUID,
				Device:      device,
				Path:        "/dev/" + device,
				ReadBytes:   stats.RdBytes,
				WriteBytes:  stats.WrBytes,
				ReadOps:     stats.RdReq,
				WriteOps:    stats.WrReq,
				ReadTimeNs:  stats.RdTotalTimes,
				WriteTimeNs: stats.WrTotalTimes,
			}
			metrics = append(metrics, m)
		}
	}

	return metrics, nil
}

// CollectNetworkStats collects network I/O statistics from libvirt
func (mc *LibvirtMetricsCollector) CollectNetworkStats(
	conn *libvirt.Connect,
	domain *libvirt.Domain,
) ([]NetworkMetrics, error) {
	domainInfo, err := domain.GetInfo()
	if err != nil {
		return nil, err
	}

	// Only collect metrics for running domains
	if domainInfo.State != libvirt.DOMAIN_RUNNING {
		return []NetworkMetrics{}, nil
	}

	domainName, err := domain.GetName()
	if err != nil {
		return nil, err
	}

	domainUUID, err := domain.GetUUIDString()
	if err != nil {
		return nil, err
	}

	var metrics []NetworkMetrics

	// Try to discover interfaces dynamically
	interfaces := mc.discoverNetworkInterfaces(domain)

	for _, ifaceName := range interfaces {
		// Get interface stats
		stats, err := domain.InterfaceStats(ifaceName)
		if err != nil {
			continue
		}

		m := NetworkMetrics{
			Name:      domainName,
			UUID:      domainUUID,
			Interface: ifaceName,
			RxBytes:   stats.RxBytes,
			TxBytes:   stats.TxBytes,
			RxPackets: stats.RxPackets,
			TxPackets: stats.TxPackets,
			RxErrors:  stats.RxErrs,
			TxErrors:  stats.TxErrs,
			RxDrops:   stats.RxDrop,
			TxDrops:   stats.TxDrop,
		}
		metrics = append(metrics, m)
	}

	return metrics, nil
}

// discoverBlockDevices attempts to discover available block devices for a domain using XML parsing
func (mc *LibvirtMetricsCollector) discoverBlockDevices(domain *libvirt.Domain) []string {
	var devices []string

	// Get domain XML description
	xmlDesc, err := domain.GetXMLDesc(0)
	if err != nil {
		log.Printf("Warning: Failed to get domain XML: %v", err)
		return mc.fallbackBlockDeviceDiscovery(domain)
	}

	// Parse the XML
	var domainXML libvirtxml.Domain
	if err := xml.Unmarshal([]byte(xmlDesc), &domainXML); err != nil {
		log.Printf("Warning: Failed to parse domain XML: %v", err)
		return mc.fallbackBlockDeviceDiscovery(domain)
	}

	// Extract disk devices from XML
	if domainXML.Devices != nil {
		for _, disk := range domainXML.Devices.Disks {
			if disk.Target != nil && disk.Target.Dev != "" {
				devices = append(devices, disk.Target.Dev)
			}
		}
	}

	// If XML parsing didn't find any devices, fall back to trial-and-error
	if len(devices) == 0 {
		return mc.fallbackBlockDeviceDiscovery(domain)
	}

	return devices
}

// fallbackBlockDeviceDiscovery uses trial-and-error method as fallback
func (mc *LibvirtMetricsCollector) fallbackBlockDeviceDiscovery(domain *libvirt.Domain) []string {
	var devices []string

	// Common block device patterns in virtualized environments
	commonDevices := []string{
		// VirtIO block devices (KVM/QEMU)
		"vda", "vdb", "vdc", "vdd", "vde", "vdf",
		// SCSI devices
		"sda", "sdb", "sdc", "sdd", "sde", "sdf",
		// IDE devices
		"hda", "hdb", "hdc", "hdd",
		// NVMe devices
		"nvme0n1", "nvme1n1", "nvme2n1",
		// Xen devices
		"xvda", "xvdb", "xvdc", "xvdd",
	}

	// Test each device to see if it exists
	for _, device := range commonDevices {
		// Try to get stats - if successful, device exists
		_, err := domain.BlockStatsFlags(device, 0)
		if err == nil {
			devices = append(devices, device)
			continue
		}
		// Try basic stats as fallback
		_, err = domain.BlockStats(device)
		if err == nil {
			devices = append(devices, device)
		}
	}

	return devices
}

// discoverNetworkInterfaces attempts to discover available network interfaces for a domain using XML parsing
func (mc *LibvirtMetricsCollector) discoverNetworkInterfaces(domain *libvirt.Domain) []string {
	var interfaces []string

	// Get domain XML description
	xmlDesc, err := domain.GetXMLDesc(0)
	if err != nil {
		log.Printf("Warning: Failed to get domain XML for interfaces: %v", err)
		return mc.fallbackNetworkInterfaceDiscovery(domain)
	}

	// Parse the XML
	var domainXML libvirtxml.Domain
	if err := xml.Unmarshal([]byte(xmlDesc), &domainXML); err != nil {
		log.Printf("Warning: Failed to parse domain XML for interfaces: %v", err)
		return mc.fallbackNetworkInterfaceDiscovery(domain)
	}

	// Extract network interfaces from XML
	if domainXML.Devices != nil {
		for _, iface := range domainXML.Devices.Interfaces {
			if iface.Target != nil && iface.Target.Dev != "" {
				interfaces = append(interfaces, iface.Target.Dev)
			}
		}
	}

	// If XML parsing didn't find any interfaces, fall back to trial-and-error
	if len(interfaces) == 0 {
		return mc.fallbackNetworkInterfaceDiscovery(domain)
	}

	return interfaces
}

// fallbackNetworkInterfaceDiscovery uses trial-and-error method as fallback
func (mc *LibvirtMetricsCollector) fallbackNetworkInterfaceDiscovery(domain *libvirt.Domain) []string {
	var interfaces []string

	// Common network interface patterns
	commonInterfaces := []string{
		// Standard Ethernet
		"eth0", "eth1", "eth2", "eth3", "eth4", "eth5",
		// Predictable interface names (systemd)
		"ens3", "ens4", "ens5", "ens6", "ens7", "ens8",
		"enp0s3", "enp0s4", "enp0s5", "enp0s6", "enp0s7", "enp0s8",
		"eno1", "eno2", "eno3", "eno4",
		// libvirt virtual interfaces
		"vnet0", "vnet1", "vnet2", "vnet3", "vnet4", "vnet5",
		// VLAN interfaces
		"eth0.1", "eth0.2", "eth1.1", "eth1.2",
		// Bridge interfaces
		"br0", "br1", "br2", "virbr0", "virbr1",
		// Wireless
		"wlan0", "wlan1", "wlp0s3", "wlp0s4",
	}

	// Test each interface to see if it exists
	for _, iface := range commonInterfaces {
		_, err := domain.InterfaceStats(iface)
		if err == nil {
			interfaces = append(interfaces, iface)
		}
	}

	return interfaces
}

// CollectDeviceStats collects device statistics from libvirt
func (mc *LibvirtMetricsCollector) CollectDeviceStats(
	conn *libvirt.Connect,
	domain *libvirt.Domain,
) (*DeviceMetrics, error) {
	domainName, err := domain.GetName()
	if err != nil {
		return nil, err
	}

	domainUUID, err := domain.GetUUIDString()
	if err != nil {
		return nil, err
	}

	metrics := &DeviceMetrics{
		Name: domainName,
		UUID: domainUUID,
	}

	// Check for TPM
	xmlDesc, err := domain.GetXMLDesc(0)
	if err == nil {
		// Simple check for TPM in XML
		if len([]byte(xmlDesc)) > 0 {
			metrics.HasTPM = false // Would need to parse XML to determine this accurately
			metrics.HasRNG = false // Would need to parse XML to determine this accurately
		}
	}

	return metrics, nil
}

// CollectJobStats collects job statistics from libvirt
func (mc *LibvirtMetricsCollector) CollectJobStats(
	conn *libvirt.Connect,
	domain *libvirt.Domain,
) (*DomainJobMetrics, error) {
	domainName, err := domain.GetName()
	if err != nil {
		return nil, err
	}

	domainUUID, err := domain.GetUUIDString()
	if err != nil {
		return nil, err
	}

	metrics := &DomainJobMetrics{
		Name: domainName,
		UUID: domainUUID,
	}

	// Try to get job info
	jobInfo, err := domain.GetJobInfo()
	if err == nil && jobInfo.Type != libvirt.DOMAIN_JOB_NONE {
		metrics.Type = jobTypeToString(jobInfo.Type)
		if jobInfo.DataTotal > 0 {
			metrics.Progress = float64(jobInfo.DataProcessed) / float64(jobInfo.DataTotal)
		}
		metrics.Remaining = jobInfo.DataRemaining
		metrics.Transferred = jobInfo.DataProcessed
		metrics.Total = jobInfo.DataTotal
		if jobInfo.DiskBpsSet {
			metrics.SpeedBps = jobInfo.DiskBps
		}
	}

	return metrics, nil
}

// CollectSnapshotStats collects snapshot statistics from libvirt
func (mc *LibvirtMetricsCollector) CollectSnapshotStats(
	conn *libvirt.Connect,
	domain *libvirt.Domain,
) (*SnapshotMetrics, error) {
	domainName, err := domain.GetName()
	if err != nil {
		return nil, err
	}

	domainUUID, err := domain.GetUUIDString()
	if err != nil {
		return nil, err
	}

	// List snapshots to get count
	snapshots, err := domain.ListAllSnapshots(0)
	if err != nil {
		return nil, err
	}

	// Free snapshots
	for _, snapshot := range snapshots {
		if err := snapshot.Free(); err != nil {
			slogx.Info("Error freeing snapshot: %v", err)
		}
	}

	metrics := &SnapshotMetrics{
		Name:  domainName,
		UUID:  domainUUID,
		Count: len(snapshots),
	}

	return metrics, nil
}

// collectBasicConnectionInfo collects basic connection information
func (mc *LibvirtMetricsCollector) collectBasicConnectionInfo(conn *libvirt.Connect) (*ConnectionMetrics, error) {
	info := &ConnectionMetrics{}
	var err error

	// Get connection URI
	info.URI, err = conn.GetURI()
	if err != nil {
		info.URI = "unknown"
	}

	// Check if connection is alive
	info.IsAlive, err = conn.IsAlive()
	if err != nil {
		info.IsAlive = false
	}

	// Get hostname
	info.Hostname, err = conn.GetHostname()
	if err != nil {
		info.Hostname = "unknown"
	}

	// Get libvirt version
	libvirtVersionInt, err := conn.GetLibVersion()
	if err != nil {
		libvirtVersionInt = 0
	}
	info.LibvirtVersion = uint64(libvirtVersionInt)

	// Get hypervisor version
	hypervisorVersionInt, err := conn.GetVersion()
	if err != nil {
		hypervisorVersionInt = 0
	}
	info.HypervisorVersion = uint64(hypervisorVersionInt)

	// Get driver type from URI
	info.DriverType = "unknown"
	if strings.HasPrefix(info.URI, "qemu") {
		info.DriverType = "qemu"
	} else if strings.HasPrefix(info.URI, "xen") {
		info.DriverType = "xen"
	} else if strings.HasPrefix(info.URI, "lxc") {
		info.DriverType = "lxc"
	}

	// Get node info
	info.NodeInfo, err = conn.GetNodeInfo()
	if err != nil {
		info.NodeInfo = &libvirt.NodeInfo{}
	}

	// Note: FreeMemory is not set here as it's not part of basic connection info
	// It will be set in CollectConnectionStats

	return info, err
}

// collectStoragePoolInfo collects storage pool information
func (mc *LibvirtMetricsCollector) collectStoragePoolInfo(conn *libvirt.Connect) ([]StoragePoolMetrics, error) {
	var storagePools []StoragePoolMetrics

	pools, err := conn.ListAllStoragePools(0)
	if err != nil {
		return storagePools, nil // Return empty slice on error
	}

	for _, pool := range pools {
		poolInfo, err := pool.GetInfo()
		if err != nil {
			if err := pool.Free(); err != nil {
				slogx.Info("Error freeing pool: %v", err)
			}
			continue
		}

		poolName, err := pool.GetName()
		if err != nil {
			if err := pool.Free(); err != nil {
				slogx.Info("Error freeing pool: %v", err)
			}
			continue
		}

		// Get volumes count by listing them
		volumeCount := 0

		state := "inactive"
		active, err := pool.IsActive()
		if err == nil && active {
			state = "active"
		}

		// Get pool type from XML description
		poolType := "unknown"
		xmlDesc, err := pool.GetXMLDesc(0)
		if err == nil {
			var poolXML libvirtxml.StoragePool
			if err := xml.Unmarshal([]byte(xmlDesc), &poolXML); err == nil {
				poolType = poolXML.Type
			}
		}

		storagePool := StoragePoolMetrics{
			Name:       poolName,
			Type:       poolType,
			State:      state,
			Capacity:   uint64(poolInfo.Capacity),
			Allocation: uint64(poolInfo.Allocation),
			Available:  uint64(poolInfo.Available),
			Volumes:    volumeCount,
		}
		storagePools = append(storagePools, storagePool)
		if err := pool.Free(); err != nil {
			slogx.Info("Error freeing pool: %v", err)
		}
	}

	return storagePools, nil
}

// collectNetworkInfo collects network information
func (mc *LibvirtMetricsCollector) collectNetworkInfo(conn *libvirt.Connect) ([]NetworkPoolMetrics, error) {
	var networks []NetworkPoolMetrics

	nets, err := conn.ListAllNetworks(0)
	if err != nil {
		return networks, nil // Return empty slice on error
	}

	for _, net := range nets {
		netName, err := net.GetName()
		if err != nil {
			if err := net.Free(); err != nil {
				slogx.Info("Error freeing network: %v", err)
			}
			continue
		}

		bridge := ""
		bridgeName, err := net.GetBridgeName()
		if err == nil {
			bridge = bridgeName
		}

		active, err := net.IsActive()
		if err != nil {
			active = false
		}

		network := NetworkPoolMetrics{
			Name:   netName,
			Active: active,
			Bridge: bridge,
		}
		networks = append(networks, network)
		if err := net.Free(); err != nil {
			slogx.Info("Error freeing network: %v", err)
		}
	}

	return networks, nil
}

// collectHostInterfaceInfo collects host interface information
func (mc *LibvirtMetricsCollector) collectHostInterfaceInfo(conn *libvirt.Connect) ([]HostInterfaceMetrics, error) {
	var interfaces []HostInterfaceMetrics

	ifaces, err := conn.ListInterfaces()
	if err != nil {
		return interfaces, nil // Return empty slice on error
	}

	for _, ifaceName := range ifaces {
		iface, err := conn.LookupInterfaceByName(ifaceName)
		if err != nil {
			continue
		}

		// Get interface statistics (simplified for now)
		// Note: libvirt go bindings don't provide direct interface stats
		// This would require parsing /proc/net/dev or using netlink
		hostInterface := HostInterfaceMetrics{
			Name:      ifaceName,
			RxBytes:   0, // TODO: Implement interface statistics collection
			TxBytes:   0,
			RxPackets: 0,
			TxPackets: 0,
		}
		interfaces = append(interfaces, hostInterface)
		if err := iface.Free(); err != nil {
			slogx.Info("Error freeing interface: %v", err)
		}
	}

	return interfaces, nil
}

// collectDomainCounts collects domain counts
func (mc *LibvirtMetricsCollector) collectDomainCounts(conn *libvirt.Connect) (activeDomains, definedDomains int, err error) {
	// Get active domains count
	activeDomainList, err := conn.ListAllDomains(libvirt.CONNECT_LIST_DOMAINS_ACTIVE)
	if err != nil {
		activeDomains = 0
	} else {
		activeDomains = len(activeDomainList)
		// Free the domains as we only need the count
		for _, domain := range activeDomainList {
			if err := domain.Free(); err != nil {
				slogx.Info("Error freeing active domain: %v", err)
			}
		}
	}

	// Get all defined domains count
	definedDomainList, err := conn.ListAllDomains(libvirt.CONNECT_LIST_DOMAINS_ACTIVE | libvirt.CONNECT_LIST_DOMAINS_INACTIVE)
	if err != nil {
		definedDomains = 0
	} else {
		definedDomains = len(definedDomainList)
		// Free the domains as we only need the count
		for _, domain := range definedDomainList {
			if err := domain.Free(); err != nil {
				slogx.Info("Error freeing defined domain: %v", err)
			}
		}
	}

	return activeDomains, definedDomains, err
}

// collectHostCPUUsage collects host CPU usage percentage from libvirt
func (mc *LibvirtMetricsCollector) collectHostCPUUsage(conn *libvirt.Connect) float64 {
	// Try to get total CPU stats first
	cpuStats, err := conn.GetCPUStats(-1, 0)
	if err != nil {
		// If we can't get CPU stats with -1, try getting stats for CPU 0 and calculate manually
		return mc.calculateCPUUsageFromSingleCore(conn)
	}

	// If utilization is directly available, use it
	if cpuStats.UtilizationSet {
		// Utilization is in centi-percent (0-10000), convert to percentage
		return float64(cpuStats.Utilization) / 100.0
	}

	// Calculate CPU usage manually from individual stats
	return mc.calculateCPUUsageFromStats(cpuStats)
}

// calculateCPUUsageFromSingleCore calculates CPU usage from the first CPU core stats
func (mc *LibvirtMetricsCollector) calculateCPUUsageFromSingleCore(conn *libvirt.Connect) float64 {
	cpuStats, err := conn.GetCPUStats(0, 0)
	if err != nil {
		return 0.0
	}

	return mc.calculateCPUUsageFromStats(cpuStats)
}

// calculateCPUUsageFromStats calculates CPU usage percentage from NodeCPUStats
func (mc *LibvirtMetricsCollector) calculateCPUUsageFromStats(cpuStats *libvirt.NodeCPUStats) float64 {
	total := uint64(0)
	idle := uint64(0)

	// Sum up all the time components
	if cpuStats.KernelSet {
		total += cpuStats.Kernel
	}
	if cpuStats.UserSet {
		total += cpuStats.User
	}
	if cpuStats.IdleSet {
		total += cpuStats.Idle
		idle = cpuStats.Idle
	}
	if cpuStats.IowaitSet {
		total += cpuStats.Iowait
		idle += cpuStats.Iowait
	}
	if cpuStats.IntrSet {
		total += cpuStats.Intr
	}

	// Calculate usage percentage: (total - idle) / total * 100
	if total > 0 {
		return float64(total-idle) / float64(total) * 100.0
	}

	return 0.0
}

// CollectConnectionStats collects connection and host level statistics
func (mc *LibvirtMetricsCollector) CollectConnectionStats(
	conn *libvirt.Connect,
) (*ConnectionMetrics, error) {
	// Collect basic connection info
	basicInfo, err := mc.collectBasicConnectionInfo(conn)
	if err != nil {
		return nil, err
	}

	// Collect domain counts
	activeDomains, definedDomains, _ := mc.collectDomainCounts(conn)

	// Collect storage pools
	storagePools, _ := mc.collectStoragePoolInfo(conn)

	// Collect networks
	networks, _ := mc.collectNetworkInfo(conn)

	// Collect host interfaces
	interfaces, _ := mc.collectHostInterfaceInfo(conn)

	// Get free memory
	freeMemory, _ := conn.GetFreeMemory()

	// Calculate host CPU usage percentage
	cpuUsagePercent := mc.collectHostCPUUsage(conn)

	metrics := &ConnectionMetrics{
		// Basic connection info
		URI:               basicInfo.URI,
		Hostname:          basicInfo.Hostname,
		DriverType:        basicInfo.DriverType,
		IsAlive:           basicInfo.IsAlive,
		LibvirtVersion:    basicInfo.LibvirtVersion,
		HypervisorVersion: basicInfo.HypervisorVersion,
		NodeInfo:          basicInfo.NodeInfo,

		// Host resource info
		ActiveDomains:       activeDomains,
		DefinedDomains:      definedDomains,
		FreeMemoryBytes:     freeMemory,
		TotalMemoryBytes:    mc.calculateTotalMemory(basicInfo.NodeInfo),
		TotalCPUs:           mc.calculateTotalCPUs(basicInfo.NodeInfo),
		HostCPUUsagePercent: cpuUsagePercent,
		StoragePools:        storagePools,
		Networks:            networks,
		Interfaces:          interfaces,
	}

	return metrics, nil
}

// calculateTotalMemory calculates total memory in bytes from NodeInfo
func (mc *LibvirtMetricsCollector) calculateTotalMemory(nodeInfo *libvirt.NodeInfo) uint64 {
	if nodeInfo == nil {
		return 0
	}
	// Convert from KB to bytes
	return uint64(nodeInfo.Memory) * 1024
}

// calculateTotalCPUs returns total CPU count from NodeInfo
func (mc *LibvirtMetricsCollector) calculateTotalCPUs(nodeInfo *libvirt.NodeInfo) uint {
	if nodeInfo == nil {
		return 0
	}
	return nodeInfo.Cpus
}

// Helper function to convert job type to string
func jobTypeToString(jobType libvirt.DomainJobType) string {
	switch jobType {
	case libvirt.DOMAIN_JOB_BOUNDED:
		return "bounded"
	case libvirt.DOMAIN_JOB_UNBOUNDED:
		return "unbounded"
	case libvirt.DOMAIN_JOB_COMPLETED:
		return "completed"
	default:
		return "none"
	}
}
